﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using Org.Apache.Zookeeper.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using ZooKeeperNet;

namespace OF.DistributeService.Core.Common
{
    public class ZooKeeperProxy : IDisposable
    {
        public static byte[] EmptyBuffer = new byte[0];
        private ZooKeeper zk = null;
        private string connectstring = null;
        private TimeSpan sessionTimeout = default(TimeSpan);
        private ZooKeeperSafeConnectWatcher watcher = null;
        private Action onReCreateZKConnection = null;
        private bool isDisposed = false;
        public ZooKeeperProxy(string connectstring, TimeSpan sessionTimeout, ZooKeeperSafeConnectWatcher watcher,
            Action onReCreateZKConnection)
        {
            this.connectstring = connectstring;
            this.sessionTimeout = sessionTimeout;
            this.watcher = watcher;
            watcher.SetZooKeeperProxy(this);
            this.onReCreateZKConnection = onReCreateZKConnection;
            zk = new ZooKeeper(connectstring, sessionTimeout, watcher);
        }

        public static bool IsValidateNodeName(string nodeName)
        {
            Regex reg = new Regex("^[a-zA-Z0-9]+$", RegexOptions.Singleline);
            return reg.IsMatch(nodeName);
        }


        public T MaxRetryCall<T>(Func<T> func)
        {
            return func();
        }

        
        public string Create(string path, byte[] data, IEnumerable<ACL> acl, CreateMode createMode)
        {
            return MaxRetryCall<string>(() => zk.Create(path, data, acl, createMode));
        }

        public void Delete(string path, int version)
        {
            MaxRetryCall<int>(() => {
                zk.Delete(path, version);
                 return 1;
            });
        }

        public Stat Exists(string path, bool watch)
        {
            return MaxRetryCall<Stat>(() => zk.Exists(path, watch));
        }

        public Stat Exists(string path, IWatcher watcher)
        {
            return MaxRetryCall<Stat>(() => zk.Exists(path, watcher));
        }

        public IEnumerable<string> GetChildren(string path, bool watch)
        {
            return MaxRetryCall<IEnumerable<string>>(() => zk.GetChildren(path, watch));
        }

        public IEnumerable<string> GetChildren(string path, IWatcher watcher)
        {
            return MaxRetryCall<IEnumerable<string>>(() => zk.GetChildren(path, watcher));
        }

        public byte[] GetData(string path, bool watch, Stat stat)
        {
            return MaxRetryCall<byte[]>(() => zk.GetData(path, watch, stat));
        }

        public byte[] GetData(string path, IWatcher watcher, Stat stat)
        {
            return MaxRetryCall<byte[]>(() => zk.GetData(path, watcher, stat));
        }

        public Stat SetData(string path, byte[] data, int version)
        {
            return MaxRetryCall<Stat>(() => zk.SetData(path, data, version));
        }

        public void Dispose()
        {
            if (zk != null)
            {
                zk.Dispose();
                zk = null;
            }
            isDisposed = true;
        }

        public bool IsDisposed()
        {
            return isDisposed;
        }


        public void SafeCreatePath(string servicePath, byte[] dataArray)
        {
            var tempArray = servicePath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            string lastPath = string.Empty;
            byte[] currentData = null;
            for (int partI = 0; partI < tempArray.Length; partI++)
            {
                string part = tempArray[partI];
                string servicePartPath = lastPath + "/" + part;
                var stat = zk.Exists(servicePartPath, false);
                if (stat == null)
                {
                    if (partI == tempArray.Length - 1)
                    {
                        if (dataArray != null)
                        {
                            currentData = dataArray;
                        }
                        else
                        {
                            currentData = EmptyBuffer;
                        }
                    }
                    else
                    {
                        currentData = EmptyBuffer;
                    }
                    string createdServicePath = zk.Create(servicePartPath, currentData, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
                }
                lastPath = servicePartPath;
            }
        }
    }

    public class BinarySearchPath
    {
        private string[] array;
        private Func<string, bool> func;

        public BinarySearchPath(string[] array, Func<string, bool> func)
        {
            this.array = array;
            this.func = func;
        }

        public int Search()
        {
            return SearchMaxExistsIndex(0, array.Length - 1);
        }

        private int SearchMaxExistsIndex(int startI, int endI)
        {
            if (startI > endI)
            {
                return -1;
            }
            int middleI = startI + ((endI - startI) / 2);
            string str = string.Join(string.Empty, array.Take(middleI + 1));
            if (func(str))
            {
                int biggerI = SearchMaxExistsIndex(middleI + 1, endI);
                if (biggerI != -1)
                {
                    return biggerI;
                }
                else
                {
                    return middleI;
                }
            }
            else
            {
                int biggerI = SearchMaxExistsIndex(middleI + 1, endI);
                if (biggerI != -1)
                {
                    return biggerI;
                }
                else
                {
                    int littleI = SearchMaxExistsIndex(startI, middleI - 1);
                    return littleI;
                }
            }
        }
    }
}
